package com.wenda.admin.abnormal;

import com.wenda.admin.entity.TbFlow;
import com.wenda.admin.mapper.FlowMapper;
import lombok.SneakyThrows;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * 异常分析核心类
 */
@Service
public class AbnormalCore {
    @Resource
    private FlowMapper flowMapper;

    private static final String LOCAL_IP = "192.168.31.224";

    private static double math1(double[] p) {
        int n = 65536;
        double H = 0;
        for (int i = 1; i < p.length; i++) {
            if (p[i] != 0) {
                H += -p[i] * (Math.log(p[i]) / Math.log(2));
            }
        }
        return H;

    }

    private static double[] set(double[] p, double x) {
        double M = x;
        double[] p1 = new double[65536];
        if(x == 0){
            for (int i = 1; i < p.length; i++) {
                p1[i] = 0;
            }
        }else {
            for (int i = 1; i < p.length; i++) {
                double m1 = p[i];
                p1[i] = m1 / M;
            }
        }

        return p1;
    }

    /**
     * 字节数出口特征熵
     *
     * @return
     */
    @SneakyThrows
    private PropertyResult byteOutEntropy(List<TbFlow> flowList) {
        double size = 0;
        double H = 0;
        double x = 0;
        double x1 = 0;
        double temp1 = 0;
        double temp2 = 0;
        int flag = 1;
        long time1 = 0;
        double[] p = new double[65536];
        double[] p2 = new double[65536];
        int j = 0;

        for (int i = 1; i < 65536; i++) {
            p[i] = 0;
        }

        for(int indexOut = 0; indexOut < flowList.size(); indexOut ++){
            for(int indexIn = indexOut; indexIn < flowList.size(); indexIn ++ ){
                TbFlow flow = flowList.get(indexIn);
                String to_port = flow.getToPort();
                String to_ip = flow.getToIp();
                String text = flow.getContent();
                Date time = flow.getCreateTime();
                int tPort;
                if (to_port != null && to_ip.equals(LOCAL_IP)) {
                    tPort = Integer.parseInt(to_port);
                    p[tPort] = text.getBytes("utf-8").length + p[tPort];
                }
                if (flag == 1) {
                    time1 = time.getTime() + (long) 60 * 1000;
                    flag = 0;
                }
                if (time.getTime() > time1) {
                    flag = 1;
                    indexOut = indexIn;
                    break;
                }
            }

            for (int i = 1; i < 65536; i++) {
                x1 = x1 + p[i];
            }
            H = math1(set(p, x1));
            p2[++j] = H;
            p = new double[65536];
            x1 = 0;
        }



        double max = p2[1];
        double min = p2[1];
        for (int i = 0; i < p2.length; i++) {
            if (p2[i] != 0) {
                if (min > p2[i]) {
                    min = p2[i];
                }
                if (max < p2[i]) {
                    max = p2[i];
                }
            }
        }

        for (int i = 0; i < p2.length; i++) {
            if (p2[i] != min && p2[i] != max && p2[i] != 0) {
                x++;
                size = size + p2[i];
                temp2 = temp2 + p2[i];
                temp1 = temp1 + Math.pow(p2[i], 2);
            }
        }

        // 除数不能为0
        if(x == 0){
            PropertyResult result = new PropertyResult();
            result.setResult(0D);
            result.setMin(0D);
            result.setMax(0D);
            return result;
        }

        temp1 = temp1 * 1 / x;
        temp2 = Math.pow(temp2, 2) * 1 / x * 1 / x;
        PropertyResult result = new PropertyResult();
        result.setResult(Math.sqrt(temp1 - temp2));
        result.setMin((size / x) - (3 * Math.sqrt(temp1 - temp2)));
        result.setMax(size / x + 3 * Math.sqrt(temp1 - temp2));
        return result;
    }

    /**
     * 报文数入口特征熵
     *
     * @param flowList
     * @return
     */
    private PropertyResult packetCountEntropy(List<TbFlow> flowList) {
        double size = 0;
        double H = 0;
        double x = 0;
        double x1 = 0;
        double temp1 = 0;
        double temp2 = 0;
        int flag = 1;
        long time1 = 0;
        double[] p = new double[65536];
        double[] p2 = new double[65536];
        int j = 0;

        for (int i = 1; i < 65536; i++) {
            p[i] = 0;
        }

        for(int indexOut = 0; indexOut < flowList.size(); indexOut ++){
            for(int indexIn = indexOut; indexIn < flowList.size(); indexIn ++ ) {
                TbFlow flow = flowList.get(indexIn);
                Date time = flow.getCreateTime();
                String to_ip = flow.getToIp();
                String to_port = flow.getToPort();

                int tPort;
                if (to_port != null && to_ip.equals(LOCAL_IP)) {
                    tPort = Integer.parseInt(to_port);

                    p[tPort] = 1 + p[tPort];
                }
                if (flag == 1) {
                    time1 = time.getTime() + (long) 60 * 1000;
                    flag = 0;
                }
                if (time.getTime() > time1) {
                    flag = 1;
                    break;
                }
            }
            for (int i = 1; i < 65536; i++) {
                x1 = x1 + p[i];
            }
            H = math1(set(p, x1));
            p2[++j] = H;
            p = new double[65536];
            x1 = 0;
        }



        //特征熵储存在p2中
        double max = p2[1];
        double min = p2[1];
        for (int i = 0; i < p2.length; i++) {
            if (p2[i] != 0) {
                if (min > p2[i]) {
                    min = p2[i];
                }
                if (max < p2[i]) {
                    max = p2[i];
                }
            }
        }
        for (int i = 0; i < p2.length; i++) {
            if (p2[i] != min && p2[i] != max && p2[i] != 0) {
                x++;
                size = size + p2[i];
                temp2 = temp2 + p2[i];
                temp1 = temp1 + Math.pow(p2[i], 2);
            }
        }

        // 除数不能为0
        if(x == 0){
            PropertyResult result = new PropertyResult();
            result.setResult(0D);
            result.setMin(0D);
            result.setMax(0D);
            return result;
        }

        temp1 = temp1 * 1 / x;
        temp2 = Math.pow(temp2, 2) * 1 / x * 1 / x;

        PropertyResult result = new PropertyResult();
        result.setResult(Math.sqrt(temp1 - temp2));
        result.setMin((size / x) - (3 * Math.sqrt(temp1 - temp2)));
        result.setMax(size / x + 3 * Math.sqrt(temp1 - temp2));
        return result;
    }

    /**
     * 字节出口数
     * @param flowList
     * @return
     */
    @SneakyThrows
    private PropertyResult byteOutCount(List<TbFlow> flowList) {
        double size = 0;
        double count = 0;
        double H = 0;
        double x = 0;
        double x1 = 0;
        double temp1 = 0;
        double temp2 = 0;
        int flag = 1;
        long time1 = 0;
        double[] p= new double[65536];
        double[] p2= new double[65536];
        int j = 0;

        for (int i = 1; i < 65536; i++) {
            p[i] = 0;
        }
        for(int indexOut = 0; indexOut < flowList.size(); indexOut ++) {
            for (int indexIn = indexOut; indexIn < flowList.size(); indexIn++) {
                TbFlow flow = flowList.get(indexIn);
                Date time = flow.getCreateTime();
                String to_ip = flow.getStartIp();
                String to_port = flow.getStartPort();
                String text = flow.getContent();
                int tPort;
                if (to_port != null && to_ip.equals(LOCAL_IP)) {
                    tPort = Integer.parseInt(to_port);
                    p[tPort] = text.getBytes("utf-8").length + p[tPort];
                }
                if (flag == 1) {
                    time1 = time.getTime() + (long) 60 * 1000;
                    flag = 0;
                }
                if (time.getTime() > time1) {
                    flag = 1;
                    break;
                }
            }

            for (int i = 1; i < 65536; i++) {
                x1 = x1 + p[i];
            }
            H = math1(set(p, x1));
            p2[++j] = H;
            p = new double[65536];
            x1 = 0;
        }
        double max = p2[1];
        double min = p2[1];
        for (int i = 0; i < p2.length; i++){
            if (p2[i] != 0) {
                if (min > p2[i]){
                    min = p2[i];
                }
                if (max < p2[i]){
                    max = p2[i];
                }
            }
        }

        for (int i = 0; i < p2.length; i++){
            if (p2[i] != min && p2[i] != max && p2[i] != 0) {
                x++;
                size = size + p2[i];
                temp2 = temp2 + p2[i];
                temp1 = temp1 + Math.pow(p2[i], 2);
            }
        }

        // 除数不能为0
        if(x == 0){
            PropertyResult result = new PropertyResult();
            result.setResult(0D);
            result.setMin(0D);
            result.setMax(0D);
            return result;
        }

        temp1 = temp1 * 1 / x;
        temp2 = Math.pow(temp2, 2) * 1 / x * 1/ x;

        PropertyResult result = new PropertyResult();
        result.setResult(Math.sqrt(temp1 - temp2));
        result.setMin((size / x) - (3 * Math.sqrt(temp1 - temp2)));
        result.setMax(size / x + 3 * Math.sqrt(temp1 - temp2));
        return result;
    }


    /**
     * 报文出口数
     * @param flowList
     * @return
     */
    private PropertyResult packetOutCount(List<TbFlow> flowList) {
        int j = 0;
        double size = 0;
        double count = 0;
        double temp1 = 0;
        double H = 0;
        double x = 0;
        double x1 = 0;
        double temp2 = 0;
        int flag = 1;
        long time1 = 0;
        double[] p= new double[65536];
        double[] p2= new double[65536];

        for (int i = 1; i < 65536; i++) {
            p[i] = 0;
        }
        for(int indexOut = 0; indexOut < flowList.size(); indexOut ++) {
            for (int indexIn = indexOut; indexIn < flowList.size(); indexIn++) {
                TbFlow flow = flowList.get(indexIn);
                Date time = flow.getCreateTime();
                String to_ip = flow.getStartIp();
                String to_port = flow.getStartPort();
                int tPort;
                if (to_port != null && to_ip.equals(LOCAL_IP)) {
                    tPort = Integer.parseInt(to_port);
                    p[tPort] = 1 + p[tPort];
                }
                if (flag == 1) {
                    time1 = time.getTime() + (long) 60 * 1000;
                    flag = 0;
                }
                if (time.getTime() > time1) {
                    flag = 1;
                    break;
                }
            }
            for (int i = 1; i < 65536; i++) {
                x1 = x1 + p[i];
            }
            H = math1(set(p, x1));
            p2[++j] = H;
            p = new double[65536];
            x1 = 0;
        }

        double max = p2[1];
        double min = p2[1];
        for (int i = 0; i < p2.length; i++){
            if (p2[i] != 0) {
                if (min > p2[i]){
                    min = p2[i];
                }
                if (max < p2[i]){
                    max = p2[i];
                }
            }
        }
        for (int i = 0; i < p2.length; i++){
            if (p2[i] != min && p2[i] != max && p2[i] != 0) {
                x++;
                size = size + p2[i];
                temp2 = temp2 + p2[i];
                temp1 = temp1 + Math.pow(p2[i], 2);
            }
        }

        // 除数不能为0
        if(x == 0){
            PropertyResult result = new PropertyResult();
            result.setResult(0D);
            result.setMin(0D);
            result.setMax(0D);
            return result;
        }

        temp1 = temp1 * 1 / x;
        temp2 = Math.pow(temp2, 2) * 1 / x * 1/ x;

        temp1 = temp1 * 1 / x;
        temp2 = Math.pow(temp2, 2) * 1 / x * 1/ x;

        PropertyResult result = new PropertyResult();
        result.setResult(Math.sqrt(temp1 - temp2));
        result.setMin((size / x) - (3 * Math.sqrt(temp1 - temp2)));
        result.setMax(size / x + (3 * Math.sqrt(temp1 - temp2)));

        return result;
    }

    /**
     * 计算四种特征熵
     * @param now
     * @return
     */
    public AbnormalResult calc(Date now){
        Date oneMinuteAgo = DateUtils.addMinutes(now,-1);
        Date thirtyMinuteAgo = DateUtils.addMinutes(now,-30);

        AbnormalResult result = new AbnormalResult();

        List<TbFlow> oneMinuteFlowList = flowMapper.flowList(oneMinuteAgo, now);
        List<TbFlow> thirtyMinuteFlowList = flowMapper.flowList(thirtyMinuteAgo, now);


        PropertyResult p1 = byteOutEntropy(oneMinuteFlowList);
        PropertyResult rangeP1 = byteOutEntropy(thirtyMinuteFlowList);
        PropertyResult byteOutEntropy = new PropertyResult();
        byteOutEntropy.setResult(p1.getResult());
        byteOutEntropy.setMin(rangeP1.getMin());
        byteOutEntropy.setMax(rangeP1.getMax());
        byteOutEntropy.setIsAbnormal(byteOutEntropy.getResult() < byteOutEntropy.getMin() || byteOutEntropy.getResult() > byteOutEntropy.getMax());
        result.setByteOutEntropy(byteOutEntropy);


        PropertyResult p2 = packetCountEntropy(oneMinuteFlowList);
        PropertyResult rangeP2 = packetCountEntropy(thirtyMinuteFlowList);
        PropertyResult packetCountEntropy = new PropertyResult();
        packetCountEntropy.setResult(p2.getResult());
        packetCountEntropy.setMin(rangeP2.getMin());
        packetCountEntropy.setMax(rangeP2.getMax());
        packetCountEntropy.setIsAbnormal(packetCountEntropy.getResult() < packetCountEntropy.getMin() || packetCountEntropy.getResult() > packetCountEntropy.getMax());
        result.setPacketCountEntropy(packetCountEntropy);


        PropertyResult p3 = byteOutCount(oneMinuteFlowList);
        PropertyResult rangeP3 = byteOutCount(thirtyMinuteFlowList);
        PropertyResult byteOutCount = new PropertyResult();
        byteOutCount.setResult(p3.getResult());
        byteOutCount.setMin(rangeP3.getMin());
        byteOutCount.setMax(rangeP3.getMax());
        byteOutCount.setIsAbnormal(byteOutCount.getResult() < byteOutCount.getMin() || byteOutCount.getResult() > byteOutCount.getMax());
        result.setByteOutCount(byteOutCount);

        PropertyResult p4 = packetOutCount(oneMinuteFlowList);
        PropertyResult rangeP4 = packetOutCount(thirtyMinuteFlowList);
        PropertyResult packetOutCount = new PropertyResult();
        packetOutCount.setResult(p4.getResult());
        packetOutCount.setMin(rangeP4.getMin());
        packetOutCount.setMax(rangeP4.getMax());
        packetOutCount.setIsAbnormal(packetOutCount.getResult() < packetOutCount.getMin() || packetOutCount.getResult() > packetOutCount.getMax());
        result.setPacketOutCount(packetOutCount);

        return result;
    }
}
